//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_Script.h".
//-------------------------------------------------------------------

#ifndef _UXS_SCRIPT_TYPES_H
#define _UXS_SCRIPT_TYPES_H

#pragma once

#include "../../stl_core/ux_stl_core.h"

// STL //
#pragma warning (push)
#pragma warning (disable: 4005)

#include <string>

#pragma warning (pop)


// AngelScript + Addons //
#include "../AngelScript/angelscript.h"
#include "../AngelScript/scriptstdstring/scriptstdstring.h"


// Library //
#if defined( PLATFORM_BASE_WINDOWS ) and not defined( _UXS_DONT_ATTACH_ANGELSCRIPT_LIB )
#	pragma comment (lib, "angelscript.lib")
#endif


namespace UX_STL
{
namespace UXScript
{
	using namespace UXTypes;

#	define AS_CALL( _func ) \
	{ \
		int __as_result = ( _func ); \
		ScriptEngine::_CheckError( __as_result, #_func, UX_FUNCTION_NAME, __FILE__, __LINE__ ); \
	}
	
#	define AS_CALL_R( _func ) \
	{ \
		int __as_result = ( _func ); \
		if ( not ScriptEngine::_CheckError( __as_result, #_func, UX_FUNCTION_NAME, __FILE__, __LINE__ ) ) \
			return false; \
	}





	//
	// Script Type Info
	//

	template <typename T>
	struct ScriptTypeInfo
	{
		typedef T	type;

		static void Name (INOUT string &)		{ STATIC_WARNING( "type not defined" ); }
		static void ArgName (INOUT string &s)	{ }
		static uint SizeOf ()					{ return sizeof(T); }
	};

#	define UX_DECL_SCRIPT_TYPE( _type, _name ) \
		template <> \
		struct ScriptTypeInfo <_type> \
		{ \
			typedef _type	type; \
			\
			static void Name (INOUT string &s)		{ s << (_name); } \
			static void ArgName (INOUT string &s)	{ s << (_name); } \
			static uint SizeOf ()					{ return sizeof(type); } \
		}

#	define DECL_SCRIPT_TYPE( _type )	UX_DECL_SCRIPT_TYPE( _type, #_type )

#	define UX_DECL_SCRIPT_WRAP( _templ, _build_name, _build_arg ) \
		template <typename T> \
		struct ScriptTypeInfo < _templ > \
		{ \
			typedef _templ				type; \
			typedef ScriptTypeInfo<T>	base_t; \
			\
			static void Name (INOUT string &s)		{ _build_name; } \
			static void ArgName (INOUT string &s)	{ _build_arg; } \
			static uint SizeOf ()					{ return sizeof(type); } \
		}
	/*
#	define UX_DECL_SCRIPT_TYPE_EXT( _type, _name, _refName, _refConstName ) \
		UX_DECL_SCRIPT_TYPE( _type, _name ); \
		UX_DECL_SCRIPT_WRAP( const _type &,	ARGS( base_t::Name( s );  s << ' ' << _refConstName ) ); \
		UX_DECL_SCRIPT_WRAP( _type &,		ARGS( base_t::Name( s );  s << ' ' << _refName		) ); \
		UX_DECL_SCRIPT_WRAP( const _type *,	ARGS( base_t::Name( s );  s << ' ' << _refConstName ) ); \
		UX_DECL_SCRIPT_WRAP( _type *,		ARGS( base_t::Name( s );  s << ' ' << _refName		) );
	*/


	template <>
	struct ScriptTypeInfo <void>
	{
		typedef void	type;

		static void Name (INOUT string &s)		{ s << "void"; }
		static void ArgName (INOUT string &s)	{ s << "void"; }
		static uint SizeOf ()					{ return 0; }
	};

	DECL_SCRIPT_TYPE( bool );
	DECL_SCRIPT_TYPE( int8 );
	DECL_SCRIPT_TYPE( uint8 );
	DECL_SCRIPT_TYPE( int16 );
	DECL_SCRIPT_TYPE( uint16 );
	DECL_SCRIPT_TYPE( int );
	DECL_SCRIPT_TYPE( uint );
	DECL_SCRIPT_TYPE( int64 );
	DECL_SCRIPT_TYPE( uint64 );
	DECL_SCRIPT_TYPE( float );
	DECL_SCRIPT_TYPE( double );
	UX_DECL_SCRIPT_TYPE( std::string, "string" );

#	undef DECL_SCRIPT_TYPE

	// only 'in' and 'out' are supported
	UX_DECL_SCRIPT_WRAP( const T,
						ARGS(
							s << "const ";
							base_t::Name( s );
						),
						ARGS(
							s << "const ";
							base_t::Name( s );
						));

	UX_DECL_SCRIPT_WRAP( const T &,
						ARGS(
							s << "const ";
							base_t::Name( s );
							s << " &";
						),
						ARGS(
							s << "const ";
							base_t::Name( s );
							s << " &in";
						));
		
	UX_DECL_SCRIPT_WRAP( T &,
						ARGS(
							base_t::Name( s );
							s << " &";
						),
						ARGS(
							base_t::Name( s );
							s << " &out";
						));
		
	UX_DECL_SCRIPT_WRAP( const T *,
						ARGS(
							s << "const ";
							base_t::Name( s );
							s << " &";
						),
						ARGS(
							s << "const ";
							base_t::Name( s );
							s << " &in";
						));
		
	UX_DECL_SCRIPT_WRAP( T *,
						ARGS(
							base_t::Name( s );
							s << " &";
						),
						ARGS(
							base_t::Name( s );
							s << " &out";
						));




	//
	// Angel Script Helper
	//

	struct AngelScriptHelper
	{
		struct SimpleRefCounter
		{
		private:
			int		_counter;

		private:
			SimpleRefCounter (const SimpleRefCounter &) {}

			void operator = (const SimpleRefCounter &) {}

		public:
			SimpleRefCounter () : _counter(1) {}
		
			void __AddRef ()
			{
				_counter++;
			}

			void __Release() 
			{ 
				if ( (--_counter) == 0 ) {
					delete this;
				}
			} 
		};

		template <typename T>
		static T * FactoryCreate ()
		{
			return new T();
		}


		template <typename T>
		static void Print (T *)
		{
			TODO("Print");
		}


		template <typename T>
		static void Constructor (AngelScript::asIScriptGeneric *gen)
		{
			new( gen->GetObject() ) T();
		}

		
		template <typename T>
		static void CopyConstructor (AngelScript::asIScriptGeneric *gen)
		{
			T * a = static_cast<T *>( gen->GetArgObject(0) );
			new( gen->GetObject() ) T(*a);
		}
		

		template <typename T>
		static void Destructor (AngelScript::asIScriptGeneric *gen)
		{
			((T *) gen->GetObject() )->~T();
		}
	};


	//
	// Script Function Descriptor
	//

	namespace _script_functions_
	{

		template <typename T>
		struct GlobalFunction
		{
			static void GetDescriptor (OUT string &, uni_c_string )	{ STATIC_WARNING( "not supported" ); }
		};


		template <typename T>
		struct MemberFunction
		{
			static void GetDescriptor (OUT string &, uni_c_string )	{ STATIC_WARNING( "not supported" ); }
		};


		//template <typename T>	struct IsVoid		{ enum { value = false }; };
		//template <>				struct IsVoid<void>	{ enum { value = true }; };


		template <typename T00 = void, typename T01 = void, typename T02 = void, typename T03 = void, typename T04 = void,
				  typename T05 = void, typename T06 = void, typename T07 = void, typename T08 = void, typename T09 = void,
				  typename T10 = void, typename T11 = void, typename T12 = void, typename T13 = void, typename T14 = void
				 >
		struct ArgsToString
		{
			typedef typename CompileTime::TypeList20< T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T10, T11, T12, T13, T14 >::type	type_list;

			template <uint Offset>
			static void Get (OUT string &str)
			{
#				define ADD_ARG( i ) \
					if ( (--first_off < 0) and not TypeDescriptor::IsVoid< T##i >::value ) { \
						ScriptTypeInfo< T##i >::ArgName( str ); \
						str << ", "; \
					}

				int			first_off	= Offset;
				uint const	len			= str.Length();

				ADD_ARG( 00 );		ADD_ARG( 01 );		ADD_ARG( 02 );		ADD_ARG( 03 );		ADD_ARG( 04 );
				ADD_ARG( 05 );		ADD_ARG( 06 );		ADD_ARG( 07 );		ADD_ARG( 08 );		ADD_ARG( 09 );
				ADD_ARG( 10 );		ADD_ARG( 11 );		ADD_ARG( 12 );		ADD_ARG( 13 );		ADD_ARG( 14 );

				if ( str.Length() > len )
				{
					str.PopBack();
					str.PopBack();
				}

#				undef ADD_ARG
			}
			
			template <uint Offset>
			static void GetArgs (OUT string &str)
			{
				str << '(';
				Get<Offset>( str );
				str << ")";
			}
		};


#		define _DECL_GLOBAL_FUNC( _template_params, _params, _ret ) \
			\
			template <_template_params> \
			struct GlobalFunction < _ret (__cdecl *) (_params) > \
			{ \
				typedef ArgsToString< _params >		args_t; \
				\
				static void GetDescriptor (OUT string &str, uni_c_string  name) \
				{ \
					ScriptTypeInfo< _ret >::Name( str ); \
					str << ' ' << name; \
					args_t::GetArgs<0>( str ); \
				} \
			}; \
			\
			template <typename C, _template_params> \
			struct MemberFunction < _ret (__thiscall C:: *) (_params) > \
			{ \
				typedef ArgsToString< _params >		args_t; \
				\
				static void GetDescriptor (OUT string &str, uni_c_string  name) \
				{ \
					GlobalFunction< _ret (*) (_params) >::GetDescriptor( str, name ); \
				} \
			}; \
			\
			template <typename C, _template_params> \
			struct MemberFunction < _ret (__thiscall C:: *) (_params) const > \
			{ \
				typedef ArgsToString< _params >		args_t; \
				\
				static void GetDescriptor (OUT string &str, uni_c_string  name) \
				{ \
					GlobalFunction< _ret (*) (_params) >::GetDescriptor( str, name ); \
					str << " const"; \
				} \
			};

		
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6, typename T7, typename T8, typename T9,
					typename T10, typename T11, typename T12, typename T13, typename T14 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 ),
			Ret );
		
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6, typename T7, typename T8, typename T9,
					typename T10, typename T11, typename T12, typename T13 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6, typename T7, typename T8, typename T9,
					typename T10, typename T11, typename T12 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6, typename T7, typename T8, typename T9,
					typename T10, typename T11 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6, typename T7, typename T8, typename T9,
					typename T10 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6, typename T7, typename T8, typename T9 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6, typename T7, typename T8 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6, T7, T8 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6, typename T7 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6, T7 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5, typename T6 ),
			ARGS( T0, T1, T2, T3, T4, T5, T6 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret,
					typename T0, typename T1, typename T2, typename T3, typename T4,
					typename T5 ),
			ARGS( T0, T1, T2, T3, T4, T5 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret, typename T0, typename T1, typename T2, typename T3, typename T4 ),
			ARGS( T0, T1, T2, T3, T4 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret, typename T0, typename T1, typename T2, typename T3 ),
			ARGS( T0, T1, T2, T3 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret, typename T0, typename T1, typename T2 ),
			ARGS( T0, T1, T2 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret, typename T0, typename T1 ),
			ARGS( T0, T1 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret, typename T0 ),
			ARGS( T0 ),
			Ret );
	
		_DECL_GLOBAL_FUNC(
			ARGS( typename Ret ),
			,
			Ret );

#		undef _DECL_GLOBAL_FUNC

	}	// _script_functions_

	

	//
	// Global Function
	//

	template <typename T>
	struct GlobalFunction : _script_functions_::GlobalFunction<T>
	{};


	//
	// Member Function
	//

	template <typename T>
	struct MemberFunction : _script_functions_::MemberFunction<T>
	{};


	//
	// Context Setter Getter
	//

	template <typename T>
	struct ContextSetterGetter
	{
		static T	Get (AngelScript::asIScriptContext *ctx)							{ STATIC_WARNING( "not supported for this type" );  return T(); }
		static int	Set (AngelScript::asIScriptContext *ctx, int arg, const T &value)	{ STATIC_WARNING( "not supported for this type" );  return 0; }
	};

#	define DECL_CONTEXT_RESULT( _type, _get, _set ) \
		template <> \
		struct ContextSetterGetter < _type > \
		{ \
			static _type  Get (AngelScript::asIScriptContext *ctx)								{ return (_type) ctx->_get(); } \
			static int    Set (AngelScript::asIScriptContext *ctx, int arg, const _type &value)	{ return ctx->_set( arg, value ); } \
		}

	DECL_CONTEXT_RESULT( int8,		GetReturnByte,		SetArgByte );
	DECL_CONTEXT_RESULT( uint8,		GetReturnByte,		SetArgByte );
	DECL_CONTEXT_RESULT( int16,		GetReturnWord,		SetArgWord );
	DECL_CONTEXT_RESULT( uint16,	GetReturnWord,		SetArgWord );
	DECL_CONTEXT_RESULT( int,		GetReturnDWord,		SetArgDWord );
	DECL_CONTEXT_RESULT( uint,		GetReturnDWord,		SetArgDWord );
	DECL_CONTEXT_RESULT( int64,		GetReturnQWord,		SetArgQWord );
	DECL_CONTEXT_RESULT( uint64,	GetReturnQWord,		SetArgQWord );
	DECL_CONTEXT_RESULT( float,		GetReturnFloat,		SetArgFloat );
	DECL_CONTEXT_RESULT( double,	GetReturnDouble,	SetArgDouble );
	
	template <typename T>
	struct ContextSetterGetter < T * >
	{
		static T *  Get (AngelScript::asIScriptContext *ctx)									{ return (T *) ctx->GetReturnAddress(); }
		static int  Set (AngelScript::asIScriptContext *ctx, int arg, const T * const & ptr)	{ return ctx->SetArgAddress( arg, (void *) ptr ); }
	};

#	undef DECL_CONTEXT_RESULT

	
//-------------------------------------------------------------------

}	// UXScript
}	// UX_STL

#endif	// _UXS_SCRIPT_TYPES_H